package com.pyb.common.sms.biz.service.impl;

import com.google.common.base.Preconditions;

import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import co.paralleluniverse.fibers.Fiber;
import co.paralleluniverse.fibers.SuspendExecution;
import co.paralleluniverse.fibers.Suspendable;
import co.paralleluniverse.fibers.httpasyncclient.FiberCloseableHttpAsyncClient;
import co.paralleluniverse.strands.SuspendableCallable;

/**
 * @author albon
 *         Date : 16-12-14
 *         Time: 上午9:44
 */
public class FiberHttpUtil {
    private static final Logger logger = LoggerFactory.getLogger(FiberHttpUtil.class);
    private static final int MAX_CONN_PER_ROUTE = 5000;
    private static final int MAX_CONN_TOTAL = 50000;
    public static final int SOCKET_TIMEOUT = 5000;
    public static final int CONNECT_TIMEOUT = 1000;

    private static CloseableHttpAsyncClient client;

    static {
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(SOCKET_TIMEOUT).setConnectTimeout(CONNECT_TIMEOUT).build();
        client = FiberCloseableHttpAsyncClient.wrap(HttpAsyncClients.custom().setMaxConnPerRoute(MAX_CONN_PER_ROUTE)
                .setMaxConnTotal(MAX_CONN_TOTAL).setDefaultRequestConfig(requestConfig).build());
        client.start();
    }

    public static String get(String url) {
        HttpGet request = new HttpGet(url);
        return execute(request, SOCKET_TIMEOUT, TimeUnit.MILLISECONDS);
    }

    public static String get(String url, int timeOut, TimeUnit timeUnit) {
        HttpGet request = new HttpGet(url);
        return execute(request, timeOut, timeUnit);
    }

    public static String execute(final HttpUriRequest request, final int timeOut, final TimeUnit timeUnit) {
        Preconditions.checkNotNull(request, "input param con't be null");

        long start = System.currentTimeMillis();
        try {
            Fiber<String> fiber = new Fiber<String>(new SuspendableCallable<String>() {

                public String run() throws SuspendExecution, InterruptedException {
                    Future<HttpResponse> future = client.execute(request, emptyCallback);

                    try {
                        HttpResponse response = future.get(timeOut, timeUnit);

                        int statusCode = response.getStatusLine().getStatusCode();
                        if (statusCode == 200) {
                            String result = EntityUtils.toString(response.getEntity()).trim();
                            return result;
                        }

                        logger.error("fiber_http_util_execute_error_status url: {}, status: {}", request.getURI(), statusCode);
                    } catch (ExecutionException | IOException | TimeoutException e) {
                        logger.error("fiber_http_util_execute_error_callable uri: {}", request.getURI(), e);
                    }

                    return null;
                }

            }).start();

            return fiber.get();
        } catch (Exception e) {
            logger.error("fiber_http_util_execute_error_get uri: {}", request.getURI(), e);
        } finally {
        }

        return null;
    }

    private static final FutureCallback<HttpResponse> emptyCallback = new FutureCallback<HttpResponse>() {
        @Override
        public void completed(HttpResponse httpResponse) {
            // do nothing
        }

        @Override
        public void failed(Exception e) {
            // do nothing
        }

        @Override
        public void cancelled() {
            // do nothing
        }
    };

    public static CloseableHttpAsyncClient getClient() {
        return client;
    }

    public static void setClient(CloseableHttpAsyncClient client) {
        FiberHttpUtil.client = client;
    }
}
